{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Building an App Engine app to serve ML prediction\n",
    "\n",
    "**Learning Objectives**\n",
    "\n",
    "1. Deploy a web application that consumes your model service on Cloud AI Platform.\n",
    "\n",
    "## Introduction \n",
    "**Verify that you have previously Trained your Keras model and Deployed it predicting with Keras model on Cloud AI Platform. If not, go back to [train_keras_ai_platform_babyweight.ipynb](../solutions/train_keras_ai_platform_babyweight.ipynb) and [deploy_keras_ai_platform_babyweight.ipynb](../solutions/deploy_keras_ai_platform_babyweight.ipynb) create them.**\n",
    "\n",
    "In the previous notebook, we deployed our model to CAIP. In this notebook, we'll make a [Flask app](https://palletsprojects.com/p/flask/) to show how our models can interact with a web application which could be deployed to [App Engine](https://cloud.google.com/appengine) with the [Flexible Environment](https://cloud.google.com/appengine/docs/flexible)."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Step 1: Review Flask App code in `application` folder\n",
    "Let's start with what our users will see. In the `application` folder, we have prebuilt the components for web application. In the templates folder, the <a href=\"application/templates/index.html\">index.html</a> file is the visual GUI our users will make predictions with.\n",
    "\n",
    "It works by using an HTML [form](https://www.w3schools.com/html/html_forms.asp) to make a [POST request](https://www.w3schools.com/tags/ref_httpmethods.asp) to our server, passing along the values captured by the [input tags](https://www.w3schools.com/html/html_form_input_types.asp).\n",
    "\n",
    "The form will render a little strangely in the notebook since the notebook environment does not run javascript, nor do we have our web server up and running. Let's get to that!"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Step 2: Set environment variables"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 34,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Your current GCP Project Name is: asl-ml-immersion\n"
     ]
    }
   ],
   "source": [
    "%%bash\n",
    "# Check your project name\n",
    "export PROJECT=$(gcloud config list project --format \"value(core.project)\")\n",
    "echo \"Your current GCP Project Name is: \"$PROJECT"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import os\n",
    "os.environ[\"BUCKET\"] = \"your-bucket-id-here\" # Recommended: use your project name"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Step 3: Complete application code in `application/main.py`\n",
    "We can set up our server with python using [Flask](https://flask.palletsprojects.com/en/1.1.x/quickstart/). Below, we've already built out most of the application for you.\n",
    "\n",
    "The `@app.route()` decorator defines a function to handle web reqests. Let's say our website is `www.example.com`. With how our `@app.route(\"/\")` function is defined, our sever will render our <a href=\"application/templates/index.html\">index.html</a> file when users go to `www.example.com/` (which is the default route for a website).\n",
    "\n",
    "So, when a user pings our server with `www.example.com/predict`, they would use `@app.route(\"/predict\", methods=[\"POST\"])` to make a prediction. The data that gets sent over the internet isn't a dictionary, but a string like below:\n",
    "\n",
    "`name1=value1&name2=value2` where `name` corresponds to the `name` on the input tag of our html form, and the value is what the user entered. Thankfully, Flask makes it easy to transform this string into a dictionary with `request.form.to_dict()`, but we still need to transform the data into a format our model expects. We've done this with the `gender2str` and the `plurality2str` utility functions.\n",
    "\n",
    "Ok! Let's set up a webserver to take in the form inputs, process them into features, and send these features to our model on Cloud AI Platform to generate predictions to serve to back to users.\n",
    "\n",
    "Fill in the **TODO** comments in <a href=\"application/main.py\">application/main.py</a>. Give it a go first and review the solutions folder if you get stuck.\n",
    "\n",
    "**Note:** AppEngine test configurations have already been set for you in the file <a href=\"application/app.yaml\">application/app.yaml</a>. Review [app.yaml](https://cloud.google.com/appengine/docs/standard/python/config/appref) documentation for additional configuration options."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Step 4: Deploy application\n",
    "So how do we know that it works? We'll have to deploy our website and find out! Notebooks aren't made for website deployment, so we'll move our operation to the [Google Cloud Shell](https://console.cloud.google.com/home/dashboard?cloudshell=true).\n",
    "\n",
    "By default, the shell doesn't have Flask installed, so copy over the following command to install it.\n",
    "\n",
    "`python3 -m pip install --user Flask==0.12.1`\n",
    "\n",
    "Next, we'll need to copy our web app to the Cloud Shell. We can use [Google Cloud Storage](https://cloud.google.com/storage) as an inbetween."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%bash\n",
    "# TODO 1: Your code goes here"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Run the below cell, and copy the output into the [Google Cloud Shell](https://console.cloud.google.com/home/dashboard?cloudshell=true)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 27,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "rm -r baby_app/\n",
      "mkdir baby_app/\n",
      "gcloud storage cp --recursive gs://asl-ml-immersion/baby_app ./\n",
      "python3 baby_app/main.py\n"
     ]
    }
   ],
   "source": [
    "%%bash\n",
    "echo rm -r baby_app/\n",
    "echo mkdir baby_app/\n",
    "echo gcloud storage cp --recursive gs://$BUCKET/baby_app ./\n",
    "echo python3 baby_app/main.py"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Step 5: Use your website to generate predictions\n",
    "Time to play with the website! The cloud shell should now say something like `* Running on http://127.0.0.1:8080/ (Press CTRL+C to quit)`. Click on the `http` link to go to your shiny new website. Fill out the form and give it a minute or two to process its first prediction. After the first one, the rest of the predictions will be lightning fast.\n",
    "\n",
    "Did you get a prediction? If not, the Google Cloud Shell will spit out a stack trace of the error to help narrow it down. If yes, congratulations! Great job on bringing all of your work together for the users."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Lab Summary\n",
    "\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "In this lab, you deployed a simple Flask web form application on App Engine that takes inputs, transforms them into features, and sends them to a model service on Cloud AI Platform to generate and return predictions."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Copyright 2019 Google Inc. Licensed under the Apache License, Version 2.0 (the \"License\"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.6"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
